<?php
require_once $GLOBALS['laravel_paths']['app']  . "libraries/database.php";

class User
{
  /** @var integer $id - id */
  public $id;
  /** @var string $email - email */
  public $nickName;
  public $email;
  /** @var string $password - hashed password as plain text */
  public $password;
  /** @var string $type - type of user (group) */
  public $type;
  public $facebookId;
  /** @var string $permissions - array of permisions, each key represent permision type) */
  /** @var datetime $dateRegistered */
  public $registrationDate;


  /* -------- USER TYPES BEGIN -------- */
  const TYPE_USER = 'user';
  const TYPE_ADMIN = 'admin';
  /* -------- USER TYPES END -------- */


  // $COLUMN_MAPPING - dictionary with column names that is used when you want to
  // automatically sort a search on a given column or search for specific column values.
  private static $COLUMN_MAPPING = array(
    'id' => 'id',
    'nickName' => 'nick_name',
    'email' => 'email',
    'password' => 'password',
    'type' => 'type',
    'facebookId' => 'facebook_id',
    'registrationDate' => 'registration_date'
  );

  /**
   * User constructor
   * @param <type> $id
   * @param <type> $user_name 
   */
  public function __construct($id = null, $user_name = null)
  {
    $this->Init();
    if (!$this->Load($id, $user_name))
      $this->Init();
  }

  private function Init()
  {
    $this->id = -1;
    $this->email = null;
    $this->password = null;
    $this->type = 'user';
  }

  /**
   * Populates basic properties from with data extracted from database.
   * @param array $data - dictionary of (<table column name> => <value>)
   */
  private function SetPropertyValues(&$data)
  {
    $this->id = $data['id'];
    $this->email = $data['email'];
    $this->type = $data['type'];
    $this->nickName = $data['nick_name'];
  }

  /**
   * Loads data by querying database by id OR email
   * @param integer $id
   * @param string $user_name
   * @return bool true if data was successfully loaded, otherwise false
   */
  private function Load($id = null, $user_name = null)
  {
    if (empty($id) && empty($user_name)) return false;
    
    $where = 1;
    $user_name = trim($user_name);
    
    if ($id)
      $where .= ' AND id=' . $id;
    else if (!empty($user_name))
      $where .= ' AND email=' . $user_name;

    $query = 'SELECT u.* FROM users AS u WHERE ' . $where;
    $rows = ExecuteQuery($query);

    if (count($rows) !== 1)
      return false;

    $this->SetPropertyValues($rows[0]);

    return true;
  }

  public function Save()
  {
    $values = array(
      'email' => $this->email,
      'type' => $this->type,
      'nick_name' =>  $this->nickName,
      'registration_date' => date('Y-m-d H:i:s',time()),
    );

    $result = false;
    if (DB::table('users')->where('id', '=', $this->id)->count() > 0)
    {
      $result = DB::table('users')->update($values);
    }
    else
    {
      $values['password'] = self::HashPassword($this->password);
      $result = DB::table('users')->insert($values);
    }
    return $result;
  }
  
  public function Delete()
  {
    if ($this->id < 0) return false;
    $where = 'id='.$this->id;
    return DeleteRow('users', $where);
  }

  /**
   * Loads Users from database.
   * @param string $where - additional SQL WHERE conditional
   * @param string $joins - additional SQL query suffix
   * @param string $suffix - additional SQL query suffix
   * @param string $additionalColumns - column names, comma separated
   * @param array $paginationData
   * @return Array<User> $cronJobs
   */
  public static function &LoadUsers($where = null, $joins = null, $suffix = null,
                  $additionalColumns = null, &$paginationData = array())
  {
    if (is_string($where) && $where != "")
      $where = "AND $where";
    else
      $where = "";
    if (!is_string($joins))
      $joins = "";
    if (!is_string($suffix))
      $suffix = "";
    if (!is_string($additionalColumns) || $additionalColumns === "")
      $additionalColumns = "";
    else
      $additionalColumns = ', ' . $additionalColumns;

    $users = array();
    $query = " SELECT u.*"
                . "$additionalColumns"
            ." FROM users AS u $joins"
            ." WHERE 1 $where $suffix";

    // if pagination data given try to execute a paginated query.
    // Note: your query must contain a global SELECT query with no LIMIT clauses
    //      (you cant apply pagination something like "SELECT {..} UNION SELECT {...}")
    if (!empty($paginationData))
      $rows = ExecutePaginatedQuery($query, $paginationData);
    else
      $rows = ExecuteQuery($query);

    for ($i = 0; $i < count($rows); $i++)
    {
      $user = new User();
      $user->SetPropertyValues($rows[$i]);
      $users[] = $user;
    }
    return $users;
  }


  private function SetPermisionsData($permissionData)
  {
    if (empty($permissionData))
      return;

    $permissions = explode('|', $permissionData);

    foreach ($permissions as $permission)
    {
      // at this point we either have "<model>:<action>,<action>" of "<permission>"
      if (stripos($permission, ':') === false)
      {
        // double check that there are no spaces left
        if (trim($permission))
          $this->permissions[trim($permission)] = true;
      }
      else
      {
        $parts = explode(':', $permission);
        $model = $parts[0];
        $actions = explode(',', $parts[1]);
        foreach ($actions as $action)
        {
          // double check that there are no spaces left
          if (trim($model) && trim($action))
            $this->permissions[trim($model)][trim($action)] = true;
        }
      }
    }
  }

  public function GetPermissionsData()
  {
    $outPermissions = array();
    foreach ($this->permissions as $permission => $permissionValue)
    {
      if (is_array($permissionValue))
      {
        $model = $permission;
        $actions = array();
        foreach ($permissionValue as $action => $actionValue)
          if ($actionValue === true)
            $actions[$action] = $action;
        if (count($actions) > 0)
        {
          $outPermissions[$model] = $model . ':' . implode(',', $actions);
        }
      }
      else if ($permissionValue === true)
      {
        $outPermissions[$permission] = $permission;
      }
    }
    return implode('|', $outPermissions);
  }

  /**
   * Validates user credentials
   * @param <type> $email - user's name
   * @param <type> $password - users password, non encrypted
   * @return <type> 
   */
  public static function ValidateCredentials($email, $password)
  {
    $where = ' WHERE email=' . ToSqlQuotedString($email)
            . ' AND password=' . ToSqlQuotedString(self::HashPassword($password));
    $count = DB::table('users')->where($where)->count();;
    return $count==1;
  }

  /**
   * Saves password on persistent instances
   * @param string $password
   * @return boolean
   */
  public function SavePassword($password)
  {
    $values = array(
      'password' => self::HashPassword($password),
    );
    if ($this->id)
      return DB::table('users')->update($values)->where('id','=',$this->id);
    else
      return false;
  }
  
  /**
   * Applies md5/sha1 on given password
   * @param <type> $password
   * @return <type>
   */
  public static function HashPassword($password)
  {
    return md5($password);
  }

  /**
   * Checks either for a specific permission or a genereric permission action
   *              (i.e. save, edit, delete, list) performed on a model.
   * @param <type> $permission
   * @param <type> $model - when given will check for a genereic action on a model.
   * @return <type> 
   */
  public function CheckPermission($permission, $model = null)
  {
    if ($model)
      return ($this->permissions[$model][$permission] || $this->IsAllMighty());
    else
      return $this->permissions[$permission] || $this->IsAllMighty();
  }

  public function IsAllMighty()
  {
    return $this->type == 'admin';
  }

  /**
   * Checks display_name for uniqueness.
   * @param <type> $displayName - name
   * @param <type> $userId - id to ignore
   * @return <type>
   */
  public static function CheckDisplayNameUniqueness($displayName, $userId = null)
  {
    $where = "nick_name=" . ToSqlQuotedString($displayName);
    if ($userId)
      $where .= " AND id<>" . ToSqlQuotedString($userId);
    return !RowExists('users', $where);
  }

  /**
   * Builds ORDER BY clause from incoming pagination data.
   * @param Array $paginationData
   * @return String $orderClause - SQL ORDER BY clause
   */
  public static function BuildOrderByClause(&$paginationData)
  {
    return BuildOrderByClause(self::$COLUMN_MAPPING, $paginationData);
  }

  /* -------- PASSWORD RESET BEGIN -------- */
  /**
   * Generates password reset code
   * @param integer $email
   * @return string
   */
  public static function GeneratePasswordResetVerificationCode($email)
  {
    $query = "SELECT u.password FROM users AS u WHERE u.email=" . ToSqlQuotedString($email);
    $rows = ExecuteQuery($query);
    $password = $rows[0]['password'];
    return md5($email . $password);
  }

  /**
   * Validates password reset code
   * @param integer $email
   * @param string $code
   * @return boolean
   */
  public static function CheckPasswordResetVerificationCode($email, $code)
  {
    return self::GeneratePasswordResetVerificationCode($email) == $code;
  }
  /* -------- PASSWORD RESET END -------- */

}
?>